package com.hjy.json;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


/**
 * 将一个json字符串解析成Json对象
 */
public class Parser {
    String parseStr;
    Integer idx;

    public Parser(String parseString) {
        this.idx = 0;
        this.parseStr = parseString;
    }

    /**
     * 跳过空白字符
     */
    void skipWhiteSpace() {
        while (parseStr.charAt(idx) == ' ' || parseStr.charAt(idx) == '\r' || parseStr.charAt(idx) == '\n' || parseStr.charAt(idx) == '\t') {
            idx++;
        }
    }

    /**
     * 返回下一个字符
     *
     * @return 下一个字符
     */
    char getNextToken() {
        skipWhiteSpace();
        if (idx == parseStr.length()) {
            throw new RuntimeException("意外的输入结束");
        }
        return parseStr.charAt(idx);
    }

    /**
     * 解析 Json 字符串, 返回Json对象
     *
     * @return Json对象
     */
    public Json Parse() {
        char ch = getNextToken();
        switch (ch) {
            case 'n':
                return parseNull();
            case 't':
            case 'f':
                return parseBool();
            case '-':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return parseNumber();
            case '"':
                return new Json(parseString());
            case '[':
                return parseArray();
            case '{':
                return parseObject();
            default:
                break;
        }

        throw new RuntimeException("在解析过程中意外的字符");
    }

    /**
     * 解析字符串
     *
     * @return
     */
    private String parseString() {
        // 脱离 "
        idx++;
        Integer pos = idx;
        while (true) {
            if (idx == parseStr.length()) {
                throw new RuntimeException("非法json字符串");
            }
            char ch = parseStr.charAt(idx++);
            if (ch == '\"') {
                break;
            }

            if (ch == '\\') {
                ch = parseStr.charAt(idx++);
                switch (ch) {
                    case 'u':
                        idx += 4;
                        break;
                    case 'b':
                    case 't':
                    case 'n':
                    case 'f':
                    case 'r':
                    case '"':
                    case '\\':
                    default:
                        break;
                }
            }
        }
        return getRangString(parseStr, pos, idx - pos - 1);
    }

    /**
     * 解析数字类型
     *
     * @return
     */
    private Json parseNumber() {
        // 保存位置
        Integer pos = idx;
        // 1. 判断正负号
        if (parseStr.charAt(idx) == '-') {
            idx++;
        }
        // 2. 判断整数部分
        if (parseStr.charAt(idx) == '0') {
            idx++;
        } else if (parseStr.charAt(idx) >= '1' && parseStr.charAt(idx) <= '9') {
            idx++;
            while (idx < parseStr.length() && parseStr.charAt(idx) >= '0' && parseStr.charAt(idx) <= '9') {
                idx++;
            }
        } else {
            throw new RuntimeException("解析 number 失败");
        }

        // 3. 判断是否是小数
        if (idx == parseStr.length() || parseStr.charAt(idx) != '.') {
            String m_int = getRangString(parseStr, pos, idx - pos);
            return new Json(Integer.parseInt(m_int));
        }

        // 是小数的话
        idx++;
        if (parseStr.charAt(idx) < '0' || parseStr.charAt(idx) > '9') {
            throw new RuntimeException("解析 number 小数失败");
        }
        while (idx < parseStr.length() && parseStr.charAt(idx) >= '0' && parseStr.charAt(idx) <= '9') {
            idx++;
        }
        String m_double = getRangString(parseStr, pos, idx - pos);
        return new Json(Double.parseDouble(m_double));
    }

    /**
     * 解析 对象
     *
     * @return
     */
    private Json parseObject() {
        // 脱落 {
        idx++;
        HashMap<String, Json> map = new HashMap<>();

        char ch = getNextToken();
        if (ch == '}') {
            return new Json(map);
        }
        while (true) {
            ch = getNextToken();
            if (ch != '"') {
                throw new RuntimeException("解析 object 字符串失败");
            }
            String key = parseString();
            ch = getNextToken();
            if (ch != ':') {
                throw new RuntimeException("解析 object : 失败");
            }
            idx++;
            Json json = Parse();
            map.put(key, json);
            ch = getNextToken();
            if (ch == '}') {
                break;
            }
            if (ch != ',') {
                throw new RuntimeException("解析 object , 失败");
            }
            idx++;
        }
        idx++;
        return new Json(map);
    }

    /**
     * 解析 array
     *
     * @return
     */
    private Json parseArray() {
        // 脱离 [
        idx++;
        ArrayList<Json> jsons = new ArrayList<>();

        char ch = getNextToken();
        if (ch == ']') {
            return new Json(jsons);
        }
        while (true) {
            // 防止空格
            getNextToken();
            // 解析出数组的一个元素放入数组
            jsons.add(Parse());
            ch = getNextToken();
            if (ch == ']') {
                break;
            }
            if (ch != ',') {
                throw new RuntimeException("解析 array 失败");
            }
            idx++;
        }
        idx++;
        return new Json(jsons);
    }

    /**
     * 解析 bool
     *
     * @return
     */
    private Json parseBool() {
        if (compareStr(parseStr, "true", idx)) {
            idx += "true".length();
            return new Json(true);
        } else if (compareStr(parseStr, "false", idx)) {
            idx += "false".length();
            return new Json(false);
        }
        throw new RuntimeException("解析 bool 失败");
    }

    /**
     * 解析 null
     *
     * @return
     */
    private Json parseNull() {
        try {
            if (compareStr(parseStr, "null", idx)) {
                return new Json();
            }
        } catch (Exception e) {
            throw new RuntimeException("解析 null 失败");
        }

        throw new RuntimeException("解析 null 失败");
    }


    /**
     * 比较字符串, 从idx下标开始的 compareStr.length() 个字符是否等于 compareStr
     *
     * @param s          源串
     * @param compareStr 需要比较的串
     * @param idx        下标
     * @return 是否比较成功
     */
    Boolean compareStr(String s, String compareStr, Integer idx) {
        for (int i = 0; i < compareStr.length(); i++) {
            if (idx + i > s.length() - 1 || s.charAt(idx + i) != compareStr.charAt(i)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 获取一个范围内的字符串
     *
     * @param s     源串
     * @param start 开始位置
     * @param num   获取多少个字符
     * @return 返回获取的字符串
     */
    String getRangString(String s, Integer start, Integer num) {
        StringBuffer buffer = new StringBuffer();
        for (int i = start; i < start + num; i++) {
            buffer.append(s.charAt(i));
        }

        return buffer.toString();
    }
}
